// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2020 Jianhui Wang <wangjianhui@loongson.cn>
 */

#include <common.h>
#include <dm.h>
#include <errno.h>
#include <i2c.h>
#include <video_bridge.h>
#include <power/regulator.h>

#include <edid.h>

/*--------------------------------
   CI2CA            I2C address
   -----------------------
   (0~400mV)----------0x72(default)
   (400mV~800mV)------0x7a
   (800mV~1200mV)-----0x90
   (1200mV~1600mV)----0x92

   (2000mV~2400mV)----0x94
   (2400mV~2800mV)----0x7e
   (2800mV~3300mV)----0x76
 ------------------------------------*/

/***************************************************
   IIC 速率不要超过100K
   如果LT8618SXB 的IIC 地址不是0x72;需要用主控的GPIO 去复位LT8618SXB，先拉低100ms，再拉高，delay100ms，再初始化LT8618SXB寄存器。

   IIC rate should not exceed 100K.
   If the IIC address of LT8618SXB is not 0x72, you need to reset LT8618SXB with the master GPIO, pull down 100ms, then pull up, delay 100ms, and initialize the LT8618SXB register.
 ****************************************************/

#define LT8618SX_ADR 0x72 // IIC Address，If CI2CA pins are low(0~400mV)

// #define _DVI_ 				// DVI output (no audio)
#define _HDMI_ // HDMI output

//Input rgb888 synchronization signal:
#define _DE_SYNC_MODE_ 			// DE + Hsync + Vsync
// #define _HV_MODE_ONLY_ 		// only Hsync + Vsync, no DE
// #define _DE_MODE_ONLY_ 		// only DE, no Hsync & Vsync

// #define _LT8618_HDCP_ 		// If HDMI encrypted output is not required, mask this macro definition

// RGB888 channel Swap
#define _TTL_BGR_ 0x70 // B0~B7[D0:D7];G0~G7[D8:D15];R0~R7[D16:D23];
#define _TTL_GBR_ 0x60 // G0~G7[D0:D7];B0~B7[D8:D15];R0~R7[D16:D23];
#define _TTL_BRG_ 0x50 // B0~B7[D0:D7];R0~R7[D8:D15];G0~G7[D16:D23];
#define _TTL_RBG_ 0x40 // R0~R7[D0:D7];B0~B7[D8:D15];G0~G7[D16:D23];
#define _TTL_GRB_ 0x30 // G0~G7[D0:D7];R0~R7[D8:D15];B0~B7[D16:D23];
#define _TTL_RGB_ 0x00 // R0~R7[D0:D7];G0~G7[D8:D15];B0~B7[D16:D23];

enum {
	RESOLUTION_480P60 = 0,
	RESOLUTION_576P50,

	RESOLUTION_720P60,
	RESOLUTION_720P50,
	RESOLUTION_720P30,
	RESOLUTION_720P25,

	RESOLUTION_1080P60,
	RESOLUTION_1080P50,
	RESOLUTION_1080P30,
	RESOLUTION_1080P25,

	RESOLUTION_1080i60,
	RESOLUTION_1080i50,

	RESOLUTION_4K30,

	RESOLUTION_800x600P60,
	RESOLUTION_1024x768P60,
	RESOLUTION_1024x600P60,
};

enum {
	CHIP_VER_U2,
	CHIP_VER_U3,
};

struct lt8618sx_priv {
	u8 resolution;
	u8 chip_ver;
	u8 use_ddrclk; // 1: DDR mode; 0: SDR (normal) mode
	u8 edid_buf[EDID_EXT_SIZE];
	u8 sink_edid[EDID_EXT_SIZE];
	u8 sink_edid2[EDID_EXT_SIZE];
};

enum {
	_32KHz = 0,
	_44d1KHz,
	_48KHz,

	_88d2KHz,
	_96KHz,
	_176Khz,
	_196KHz
};

u16 IIS_N[] = {
		4096,  // 32K
		6272,  // 44.1K
		6144,  // 48K
		12544, // 88.2K
		12288, // 96K
		25088, // 176K
		24576  // 196K
};

u8 Sample_Freq[] = {
		0x30, // 32K
		0x00, // 44.1K
		0x20, // 48K
		0x80, // 88.2K
		0xa0, // 96K
		0xc0, // 176K
		0xe0  //  196K
};

static u8 LT8618SXB_PLL_u3[3][3] = {
		{0x00, 0x9e, 0xaa}, // < 50MHz
		{0x00, 0x9e, 0x99}, // 50 ~ 100M
		{0x00, 0x9e, 0x88}, // > 100M
};

static u8 LT8618SXB_PLL_u2[3][3] = {
		{0x00, 0x94, 0xaa}, // < 50MHz
		{0x01, 0x94, 0x99}, // 50 ~ 100M
		{0x03, 0x94, 0x88}, // > 100M
};

enum {
	CLK_LESS_THAN_50M = 0x00,
	CLK_BOUND_50_100M,
	CLK_GREATER_THAN_100M
};

#define PIC_RATIO_16_9 0x2A // 16:9
#define PIC_RATIO_4_3 0x19	// 4:3

//  With different resolutions, Vic has different values, Refer to the following list
/*************************************
   Resolution			VIC_Num ( VIC: Video Format Identification Code )
   --------------------------------------
   640x480				1
   720x480P 60Hz		2
   720x480i 60Hz		6
   720x576P 50Hz		17
   720x576i 50Hz		21
   1280x720P 24Hz		60
   1280x720P 25Hz		61
   1280x720P 30Hz		62
   1280x720P 50Hz		19
   1280x720P 60Hz		4
   1920x1080P 24Hz		32
   1920x1080P 25Hz		33
   1920x1080P 30Hz		34
   1920x1080i 50Hz		20
   1920x1080i 60Hz		5
   1920x1080P 50Hz		31
   1920x1080P 60Hz		16
   3840x2160 30Hz		95 // 4K30
   Other resolution     0(default) // Such as 800x600 / 1024x768 / 1366x768 / 1280x1024.....
 **************************************/

#ifndef _DE_SYNC_MODE_ // Only Hsync and Vsync synchronization signals, no DE synchronization signal
static u32 lt8618sx_format_timing[][14] =
	{
		//	H_FP /	H_sync / H_BP / H_act / H_total / V_FP / V_sync / V_BP / V_act / V_total / Vic / Pic_Ratio / Clk_bound(SDR) / Clk_bound(DDR)

		// /*------------------------------------------------    ---*/  Vic / Pic_Ratio / Clk_bound(SDR) / Clk_bound(DDR)
		{16, 62, 60, 720, 858, 9, 6, 30, 480, 525, 2, PIC_RATIO_4_3, CLK_LESS_THAN_50M, CLK_LESS_THAN_50M},	 // 480P 60Hz 27MHz
		{12, 64, 68, 720, 864, 5, 5, 39, 576, 625, 17, PIC_RATIO_4_3, CLK_LESS_THAN_50M, CLK_LESS_THAN_50M}, // 576P 50Hz 27MHz

		{110, 40, 220, 1280, 1650, 5, 5, 20, 720, 750, 4, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M},   // 720P 60Hz 74.25MHz
		{440, 40, 220, 1280, 1980, 5, 5, 20, 720, 750, 19, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M},  // 720P 50Hz 74.25MHz
		{1760, 40, 220, 1280, 3300, 5, 5, 20, 720, 750, 62, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // 720P 30Hz 74.25MHz
		{2420, 40, 220, 1280, 3960, 5, 5, 20, 720, 750, 61, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // 720P 25Hz 74.25MHz

		{88, 44, 148, 1920, 2200, 4, 5, 36, 1080, 1125, 16, PIC_RATIO_16_9, CLK_GREATER_THAN_100M, CLK_BOUND_50_100M},	// 1080P  60Hz 148.5MHz
		{528, 44, 148, 1920, 2640, 4, 5, 36, 1080, 1125, 31, PIC_RATIO_16_9, CLK_GREATER_THAN_100M, CLK_BOUND_50_100M}, // 1080P  50Hz 148.5MHz
		{88, 44, 148, 1920, 2200, 4, 5, 36, 1080, 1125, 34, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M},		// 1080P  30Hz 74.25MHz
		{528, 44, 148, 1920, 2640, 4, 5, 36, 1080, 1125, 33, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M},		// 1080P  25Hz 74.25MHz

		{88, 44, 148, 1920, 2200, 2, 5, 15, 540, 562, 5, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M},	  // 1080i  60Hz 74.25MHz
		{528, 44, 148, 1920, 2640, 2, 5, 15, 540, 562, 20, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // 1080i  50Hz 74.25MHz

		{176, 88, 296, 3840, 4400, 8, 10, 72, 2160, 2250, 95, PIC_RATIO_16_9, CLK_GREATER_THAN_100M, CLK_GREATER_THAN_100M}, // 4K 30Hz	297MHz

		{40, 128, 88, 800, 1056, 1, 4, 23, 600, 628, 0, PIC_RATIO_4_3, CLK_LESS_THAN_50M, CLK_LESS_THAN_50M},	// VESA 800x600 40MHz
		{24, 136, 160, 1024, 1344, 3, 6, 29, 768, 806, 0, PIC_RATIO_4_3, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // VESA 1024X768 65MHz
		{24, 136, 160, 1024, 1344, 3, 6, 29, 768, 806, 0, PIC_RATIO_4_3, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // VESA 1024X600 51MHz
};

enum
{
	H_fp = 0,
	H_sync,
	H_bp,
	H_act,
	H_tol,

	V_fp,
	V_sync,
	V_bp,
	V_act,
	V_tol,

	Vic,

	Pic_Ratio, // Image proportion

	Clk_bound_SDR, // SDR
	Clk_bound_DDR  // DDR
};
#else // There are Hsync, Vsync and DE synchronization signals
static u32 lt8618sx_format_timing[][4] =
	{
		//	H_FP /	H_sync / H_BP / H_act / H_total / V_FP / V_sync / V_BP / V_act / V_total / Vic / Pic_Ratio / Clk_bound(SDR) / Clk_bound(DDR)

		// /*------------------------------------------------    ---*/  Vic / Pic_Ratio / Clk_bound(SDR) / Clk_bound(DDR)
		{/* 16,	 62,  60,  720,	 858,  9, 6,  30, 480,	525,  */ 2, PIC_RATIO_4_3, CLK_LESS_THAN_50M, CLK_LESS_THAN_50M},  // 480P 60Hz 27MHz
		{/* 12,	 64,  68,  720,	 864,  5, 5,  39, 576,	625,  */ 17, PIC_RATIO_4_3, CLK_LESS_THAN_50M, CLK_LESS_THAN_50M}, // 576P 50Hz 27MHz

		{/* 110,	 40,  220, 1280, 1650, 5, 5,  20, 720,	750,  */ 4, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M},  // 720P 60Hz 74.25MHz
		{/* 440,	 40,  220, 1280, 1980, 5, 5,  20, 720,	750,  */ 19, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // 720P 50Hz 74.25MHz
		{/* 1760, 40,  220, 1280, 3300, 5, 5,  20, 720,	750,  */ 62, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // 720P 30Hz 74.25MHz
		{/* 2420, 40,  220, 1280, 3960, 5, 5,  20, 720,	750,  */ 61, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // 720P 25Hz 74.25MHz

		{/* 88,	 44,  148, 1920, 2200, 4, 5,  36, 1080, 1125, */ 16, PIC_RATIO_16_9, CLK_GREATER_THAN_100M, CLK_BOUND_50_100M}, // 1080P  60Hz 148.5MHz
		{/* 528,	 44,  148, 1920, 2640, 4, 5,  36, 1080, 1125, */ 31, PIC_RATIO_16_9, CLK_GREATER_THAN_100M, CLK_BOUND_50_100M}, // 1080P  50Hz 148.5MHz
		{/* 88,	 44,  148, 1920, 2200, 4, 5,  36, 1080, 1125, */ 34, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M},	   // 1080P  30Hz 74.25MHz
		{/* 528,	 44,  148, 1920, 2640, 4, 5,  36, 1080, 1125, */ 33, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M},	   // 1080P  25Hz 74.25MHz

		{/* 88,	 44,  148, 1920, 2200, 2, 5,  15, 540,	562,  */ 5, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M},  // 1080i  60Hz 74.25MHz
		{/* 528,	 44,  148, 1920, 2640, 2, 5,  15, 540,	562,  */ 20, PIC_RATIO_16_9, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // 1080i  50Hz 74.25MHz

		{/* 176,	 88,  296, 3840, 4400, 8, 10, 72, 2160, 2250, */ 95, PIC_RATIO_16_9, CLK_GREATER_THAN_100M, CLK_GREATER_THAN_100M}, // 4K 30Hz	297MHz

		{/* 40,	 128, 88,  800,	 1056, 1, 4,  23, 600,	628,  */ 0, PIC_RATIO_4_3, CLK_LESS_THAN_50M, CLK_LESS_THAN_50M}, // VESA 800x600 40MHz
		{/* 24,	 136, 160, 1024, 1344, 3, 6,  29, 768,	806,  */ 0, PIC_RATIO_4_3, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // VESA 1024X768 65MHz
		{/* 24,	 136, 160, 1024, 1344, 3, 6,  29, 768,	806,  */ 0, PIC_RATIO_4_3, CLK_BOUND_50_100M, CLK_LESS_THAN_50M}, // VESA 1024X600 51MHz
};

enum
{
	//	H_fp = 0,
	//	H_sync,
	//	H_bp,
	//	H_act,
	//	H_tol,

	//	V_fp,
	//	V_sync,
	//	V_bp,
	//	V_act,
	//	V_tol,

	Vic = 0,

	Pic_Ratio, // Image proportion

	Clk_bound_SDR, // SDR
	Clk_bound_DDR  // DDR
};
#endif

static void lt8618sx_read_bytes(struct udevice *dev, u8 reg, u8 *buf, int len)
{
	dm_i2c_read(dev, reg, buf, len);
}

static u8 lt8618sx_read_reg(struct udevice *dev, u8 reg)
{
	u8 data;
	dm_i2c_read(dev, reg, &data, 1);
	return data;
}

static void lt8618sx_write_reg(struct udevice *dev, u8 reg, u8 val)
{
	u8 data = val;
	dm_i2c_write(dev, reg, &data, 1);
}

#ifdef _LT8618_HDCP_
void LT8618SXB_HDCP_Init(struct udevice *dev)
{
	lt8618sx_write_reg(dev, 0xff, 0x80);
	lt8618sx_write_reg(dev, 0x13, 0xfe);
	lt8618sx_write_reg(dev, 0x13, 0xff);

	lt8618sx_write_reg(dev, 0x14, 0x00);
	lt8618sx_write_reg(dev, 0x14, 0xff);

	lt8618sx_write_reg(dev, 0xff, 0x85);
	lt8618sx_write_reg(dev, 0x07, 0x1f);
	lt8618sx_write_reg(dev, 0x13, 0xfe); // [7]=force_hpd, [6]=force_rsen, [5]=vsync_pol, [4]=hsync_pol,
	// [3]=hdmi_mode, [2]=no_accs_when_rdy, [1]=skip_wt_hdmi
	lt8618sx_write_reg(dev, 0x17, 0x0f); // [7]=ri_short_read, [3]=sync_pol_mode, [2]=srm_chk_done,
	// [1]=bksv_srm_pass, [0]=ksv_list_vld
	lt8618sx_write_reg(dev, 0x15, 0x05);
	//lt8618sx_write_reg(dev,0x15,0x65);// [7]=key_ddc_st_sel, [6]=tx_hdcp_en,[5]=tx_auth_en, [4]=tx_re_auth
}

void LT8618SXB_HDCP_Enable(struct udevice *dev)
{
	lt8618sx_write_reg(dev, 0xff, 0x80);
	lt8618sx_write_reg(dev, 0x14, 0x00);
	lt8618sx_write_reg(dev, 0x14, 0xff);

	lt8618sx_write_reg(dev, 0xff, 0x85);
	lt8618sx_write_reg(dev, 0x15, 0x01); //disable HDCP
	lt8618sx_write_reg(dev, 0x15, 0x71); //enable HDCP
	lt8618sx_write_reg(dev, 0x15, 0x65); //enable HDCP
}

void LT8618SXB_HDCP_Disable(struct udevice *dev)
{
	lt8618sx_write_reg(dev, 0xff, 0x85);
	lt8618sx_write_reg(dev, 0x15, 0x45); //enable HDCP
}
#endif

void lt8618sx_get_edid(struct udevice *dev)
{
	struct lt8618sx_priv *priv = dev_get_priv(dev);
	u8 i;
	u8 extended_flag = 0x00;

	lt8618sx_write_reg(dev, 0xff, 0x85);
	//lt8618sx_write_reg(dev,0x02,0x0a); 					//I2C 100K
	lt8618sx_write_reg(dev, 0x03, 0xc9);
	lt8618sx_write_reg(dev, 0x04, 0xa0); //0xA0 is EDID device address
	lt8618sx_write_reg(dev, 0x05, 0x00); //0x00 is EDID offset address
	lt8618sx_write_reg(dev, 0x06, 0x20); //length for read
	lt8618sx_write_reg(dev, 0x14, 0x7f);

	for (i = 0; i < 8; i++) // block 0 & 1
	{
		lt8618sx_write_reg(dev, 0x05, i * 32); //0x00 is EDID offset address
		lt8618sx_write_reg(dev, 0x07, 0x36);
		lt8618sx_write_reg(dev, 0x07, 0x34);	 //0x31
		lt8618sx_write_reg(dev, 0x07, 0x37);	 //0x37
		mdelay(5);								 // wait 5ms for reading edid data.
		if (lt8618sx_read_reg(dev, 0x40) & 0x02) //KEY_DDC_ACCS_DONE=1
		{
			if (lt8618sx_read_reg(dev, 0x40) & 0x50) //DDC No Ack or Abitration lost
			{
				printf("\r\nread edid failed: no ack");
				goto end;
			}
			else
			{
				printf("\r\n");
				// for (j = 0; j < 32; j++) {
				// 	priv->sink_edid[i * 32 + j] = lt8618sx_read_reg(dev, 0x83 );
				// 	printf( " ", priv->sink_edid[i * 32 + j] );
				// 	if(i == 3 && j == 30) {
				// 		extended_flag = priv->sink_edid[i * 32 + j] & 0x03;
				// 	}
				// }
				lt8618sx_read_bytes(dev, 0x83, &priv->sink_edid[i * 32], 32);
				if (i == 3)
				{
					extended_flag = priv->sink_edid[i * 32 + 30] & 0x03;
					if (extended_flag < 1) //no block 1, stop reading edid.
						goto end;
				}
			}
		}
		else
		{
			printf("\r\nread edid failed: accs not done");
			goto end;
		}
	}

	if (extended_flag < 2) //no block 2, stop reading edid.
	{
		goto end;
	}

	for (i = 0; i < 8; i++) //	// block 2 & 3
	{
		lt8618sx_write_reg(dev, 0x05, i * 32);	 //0x00 is EDID offset address
		lt8618sx_write_reg(dev, 0x07, 0x76);	 //0x31
		lt8618sx_write_reg(dev, 0x07, 0x74);	 //0x31
		lt8618sx_write_reg(dev, 0x07, 0x77);	 //0x37
		mdelay(5);								 // wait 5ms for reading edid data.
		if (lt8618sx_read_reg(dev, 0x40) & 0x02) //KEY_DDC_ACCS_DONE=1
		{
			if (lt8618sx_read_reg(dev, 0x40) & 0x50) //DDC No Ack or Abitration lost
			{
				printf("\r\nread edid failed: no ack");
				goto end;
			}
			else
			{
				// printf( "\r\n" );
				// for( j = 0; j < 32; j++ )
				// {
				// 	priv->sink_edid2[i * 32 + j] = lt8618sx_read_reg(dev, 0x83 );
				// 	printf( " ", priv->sink_edid2[i * 32 + j] );
				// }
				lt8618sx_read_bytes(dev, 0x83, &priv->sink_edid2[i * 32], 32);
				if (i == 3)
				{
					if (extended_flag < 3) //no block 1, stop reading edid.
					{
						goto end;
					}
				}
			}
		}
		else
		{
			printf("\r\nread edid failed: accs not done");
			goto end;
		}
	}
	debug("\r\nread edid succeeded, checksum = 0x%x", priv->sink_edid[255]);

end:
	lt8618sx_write_reg(dev, 0x03, 0xc2);
	lt8618sx_write_reg(dev, 0x07, 0x1f);
}

void lt8618sx_chip_id(struct udevice *dev)
{
	struct lt8618sx_priv *priv = dev_get_priv(dev);
	u8 id0 = 0, id1 = 0, id2 = 0;

	lt8618sx_write_reg(dev, 0xFF, 0x80); // register bank
	lt8618sx_write_reg(dev, 0xee, 0x01);

	while ((id0 != 0x17) || (id1 != 0x02) || ((id2 & 0xfc) != 0xe0))
	{
		id0 = lt8618sx_read_reg(dev, 0x00);
		id1 = lt8618sx_read_reg(dev, 0x01);
		id2 = lt8618sx_read_reg(dev, 0x02);
		mdelay(100);
	}

	debug("LT8618SXB Chip ID = 0x%x %x %x", lt8618sx_read_reg(dev, 0x00), // 0x17
		  lt8618sx_read_reg(dev, 0x01),									  // 0x02
		  lt8618sx_read_reg(dev, 0x02));								  // 0xE1 / 0xE2

	/********************
   	 _Ver_U2 ID: 0x17 0x02 0xE1
   	 _Ver_U3 ID: 0x17 0x02 0xE2
 	*********************/
	if (id2 == 0xe2)
	{
		printf("LT8618SXB chip ver3\n");
		priv->chip_ver = CHIP_VER_U3;
	}
	else if (id2 == 0xe1)
	{
		printf("LT8618SXB chip ver2\n");
		priv->chip_ver = CHIP_VER_U2;
	}
}

void LT8618SXB_RST_PD_Init(struct udevice *dev)
{
	lt8618sx_write_reg(dev, 0xff, 0x80);
	lt8618sx_write_reg(dev, 0x11, 0x00); //reset MIPI Rx logic.

	lt8618sx_write_reg(dev, 0x13, 0xf1);
	lt8618sx_write_reg(dev, 0x13, 0xf9); // Reset TTL video process
}

void LT8618SXB_TTL_Input_Analog(struct udevice *dev)
{
	// TTL mode
	lt8618sx_write_reg(dev, 0xff, 0x81); // register bank
	lt8618sx_write_reg(dev, 0x02, 0x66);
	lt8618sx_write_reg(dev, 0x0a, 0x06);
	lt8618sx_write_reg(dev, 0x15, 0x06);

	lt8618sx_write_reg(dev, 0x4e, 0xa8); // for U2

	lt8618sx_write_reg(dev, 0xff, 0x82);
	lt8618sx_write_reg(dev, 0x1b, 0x77);
	lt8618sx_write_reg(dev, 0x1c, 0xEC); // 25000
}

void LT8618SXB_TTL_Input_Digtal(struct udevice *dev)
{
	struct lt8618sx_priv *priv = dev_get_priv(dev);

	lt8618sx_write_reg(dev, 0xff, 0x80);

#ifdef _HV_MODE_ONLY_
	lt8618sx_write_reg(dev, 0x0A, 0xa0); // bit5=1 : Internal generate DE control logic clock enable
#else
#ifdef _DE_MODE_ONLY_
	lt8618sx_write_reg(dev, 0x0A, 0x90); // bit4=1 : Internal generate sync control logic clock enable
#else
	lt8618sx_write_reg(dev, 0x0A, 0x80); // normal
#endif
#endif

	// TTL_Input_Digtal
	lt8618sx_write_reg(dev, 0xff, 0x82); // register bank
	// lt8618sx_write_reg(dev, 0x45, _TTL_RGB_ );    //RGB channel swap
	lt8618sx_write_reg(dev, 0x45, _TTL_BGR_);

	if (priv->use_ddrclk)
		lt8618sx_write_reg(dev, 0x4f, 0x80); //0x00;  0x40: dclk
	else
		lt8618sx_write_reg(dev, 0x4f, 0x40); //0x00;  0x40: dclk

	lt8618sx_write_reg(dev, 0x50, 0x00);

#ifdef _HV_MODE_ONLY_
	lt8618sx_write_reg(dev, 0x47, 0x87); // bit7=1 : TTL output de from internal generated signal enable
#endif

#ifdef _DE_MODE_ONLY_
	lt8618sx_write_reg(dev, 0x47, 0x47); // bit6=1 : TTL output sync from internal generated signal enable
#endif

#ifdef _DE_SYNC_MODE_
	lt8618sx_write_reg(dev, 0x47, 0x07); // bit6=1 : TTL output sync from internal generated signal enable
#endif
}

#ifndef _DE_SYNC_MODE_
void LT8618SX_Timing_Set(struct udevice *dev)
{
	struct lt8618sx_priv *priv = dev_get_priv(dev);

	lt8618sx_write_reg(dev, 0xff, 0x82);
	lt8618sx_write_reg(dev, 0x2c, (u8)(lt8618sx_format_timing[priv->resolution][H_tol] / 256));
	lt8618sx_write_reg(dev, 0x2d, (u8)(lt8618sx_format_timing[priv->resolution][H_tol] % 256));
	lt8618sx_write_reg(dev, 0x2e, (u8)(lt8618sx_format_timing[priv->resolution][H_act] / 256));
	lt8618sx_write_reg(dev, 0x2f, (u8)(lt8618sx_format_timing[priv->resolution][H_act] % 256));
	lt8618sx_write_reg(dev, 0x30, (u8)(lt8618sx_format_timing[priv->resolution][H_fp] / 256));
	lt8618sx_write_reg(dev, 0x31, (u8)(lt8618sx_format_timing[priv->resolution][H_fp] % 256));
	lt8618sx_write_reg(dev, 0x32, (u8)(lt8618sx_format_timing[priv->resolution][H_bp] / 256));
	lt8618sx_write_reg(dev, 0x33, (u8)(lt8618sx_format_timing[priv->resolution][H_bp] % 256));
	lt8618sx_write_reg(dev, 0x34, (u8)(lt8618sx_format_timing[priv->resolution][H_sync] / 256));
	lt8618sx_write_reg(dev, 0x35, (u8)(lt8618sx_format_timing[priv->resolution][H_sync] % 256));

	lt8618sx_write_reg(dev, 0x36, (u8)(lt8618sx_format_timing[priv->resolution][V_act] / 256));
	lt8618sx_write_reg(dev, 0x37, (u8)(lt8618sx_format_timing[priv->resolution][V_act] % 256));
	lt8618sx_write_reg(dev, 0x38, (u8)(lt8618sx_format_timing[priv->resolution][V_fp] / 256));
	lt8618sx_write_reg(dev, 0x39, (u8)(lt8618sx_format_timing[priv->resolution][V_fp] % 256));
	lt8618sx_write_reg(dev, 0x3a, (u8)(lt8618sx_format_timing[priv->resolution][V_bp] / 256));
	lt8618sx_write_reg(dev, 0x3b, (u8)(lt8618sx_format_timing[priv->resolution][V_bp] % 256));
	lt8618sx_write_reg(dev, 0x3c, (u8)(lt8618sx_format_timing[priv->resolution][V_sync] / 256));
	lt8618sx_write_reg(dev, 0x3d, (u8)(lt8618sx_format_timing[priv->resolution][V_sync] % 256));
}
#endif

// The threshold value of high HPD detected by lt8618sxb is 1.2V
u8 LT8618SXB_HPD_status(struct udevice *dev)
{
	u8 HPD_Status = 0;

	lt8618sx_write_reg(dev, 0xff, 0x82); // Register bank

	if ((lt8618sx_read_reg(dev, 0x5e) & 0x05) == 0x05)
	{
		HPD_Status = 1; // HPD is High
	}

	return HPD_Status;
}

void LT8618SXB_HDMI_TX_En(struct udevice *dev, bool enable)
{
	lt8618sx_write_reg(dev, 0xff, 0x81);
	if (enable)
	{
		lt8618sx_write_reg(dev, 0x30, 0xea);
	}
	else
	{
		lt8618sx_write_reg(dev, 0x30, 0x00);
	}
}

// for debug
void LT8618SXB_Video_Check(struct udevice *dev)
{
	u8 temp;
	u16 hfp, hs_width, hbp, h_act, h_tal, v_act, v_tal, vfp, vs_width, vbp;
	u32 CLK_Cnt;
	bool __maybe_unused vs_pol, hs_pol;

	vs_pol = 0;
	hs_pol = 0;

	mdelay(100);

	lt8618sx_write_reg(dev, 0xff, 0x82); //video check

	temp = lt8618sx_read_reg(dev, 0x70); //hs vs polarity

	if (temp & 0x02)
	{
		vs_pol = 1;
	}
	if (temp & 0x01)
	{
		hs_pol = 1;
	}

	vs_width = lt8618sx_read_reg(dev, 0x71);

	hs_width = lt8618sx_read_reg(dev, 0x72) * 0x100 + lt8618sx_read_reg(dev, 0x73);

	vbp = lt8618sx_read_reg(dev, 0x74);
	vfp = lt8618sx_read_reg(dev, 0x75);
	hbp = lt8618sx_read_reg(dev, 0x76) * 0x100 + lt8618sx_read_reg(dev, 0x77);
	hfp = lt8618sx_read_reg(dev, 0x78) * 0x100 + lt8618sx_read_reg(dev, 0x79);
	v_tal = lt8618sx_read_reg(dev, 0x7a) * 0x100 + lt8618sx_read_reg(dev, 0x7b);
	h_tal = lt8618sx_read_reg(dev, 0x7c) * 0x100 + lt8618sx_read_reg(dev, 0x7d);
	v_act = lt8618sx_read_reg(dev, 0x7e) * 0x100 + lt8618sx_read_reg(dev, 0x7f);
	h_act = lt8618sx_read_reg(dev, 0x80) * 0x100 + lt8618sx_read_reg(dev, 0x81);
	CLK_Cnt = (lt8618sx_read_reg(dev, 0x1d) & 0x0f) * 0x10000 + lt8618sx_read_reg(dev, 0x1e) * 0x100 + lt8618sx_read_reg(dev, 0x1f);
	// Pixel CLK =	CLK_Cnt * 1000

	debug("h_FrontPorch = %d\n", hfp);
	debug("h_SyncWidth = %d\n", hs_width);
	debug("h_BackPorch = %d\n", hbp);
	debug("h_active = %d\n", h_act);
	debug("h_total = %d\n", h_tal);
	debug("v_FrontPorch = %d\n", vfp);
	debug("v_SyncWidth = %d\n", vs_width);
	debug("v_BackPorch = %d\n", vbp);
	debug("v_active = %d\n", v_act);
	debug("v_total = %d\n", v_tal);
	debug("Pixel CLK = %d\n", CLK_Cnt * 1000);
}

void LT8618SXB_PLL_config(struct udevice *dev)
{
	struct lt8618sx_priv *priv = dev_get_priv(dev);
	u8 clk_bound = (u8)lt8618sx_format_timing[priv->resolution][Clk_bound_SDR + priv->use_ddrclk];
	u8 read_val, cali_done, cali_val, lock;
	u8 j;

	lt8618sx_write_reg(dev, 0xff, 0x81);
	lt8618sx_write_reg(dev, 0x23, 0x40);
	lt8618sx_write_reg(dev, 0x24, 0x64); //icp set
	lt8618sx_write_reg(dev, 0x26, 0x55);

	lt8618sx_write_reg(dev, 0x29, 0x04); //for U3 for U3 SDR/DDR fixed phase

	if (priv->chip_ver == CHIP_VER_U3)
	{
		lt8618sx_write_reg(dev, 0x25, LT8618SXB_PLL_u3[clk_bound][0]);
		lt8618sx_write_reg(dev, 0x2c, LT8618SXB_PLL_u3[clk_bound][1]);
		lt8618sx_write_reg(dev, 0x2d, LT8618SXB_PLL_u3[clk_bound][2]);
	}
	else
	{
		lt8618sx_write_reg(dev, 0x25, LT8618SXB_PLL_u2[clk_bound][0]);
		lt8618sx_write_reg(dev, 0x2c, LT8618SXB_PLL_u2[clk_bound][1]);
		lt8618sx_write_reg(dev, 0x2d, LT8618SXB_PLL_u2[clk_bound][2]);
	}

	if (priv->use_ddrclk)
	{
		if (priv->chip_ver == CHIP_VER_U3)
		{
			lt8618sx_write_reg(dev, 0x4d, 0x05);
			lt8618sx_write_reg(dev, 0x27, 0x60); //0x60 //ddr 0x66
			lt8618sx_write_reg(dev, 0x28, 0x88);
		} else {
			read_val = lt8618sx_read_reg(dev, 0x2c) & 0x7f;
			read_val = read_val * 2 | 0x80;
			lt8618sx_write_reg(dev, 0x2c, read_val);

			lt8618sx_write_reg(dev, 0x4d, 0x04);
			lt8618sx_write_reg(dev, 0x27, 0x60); //0x60
			lt8618sx_write_reg(dev, 0x28, 0x88);
		}
		debug("\r\n PLL DDR");
	}
	else
	{
		if (priv->chip_ver == CHIP_VER_U3)
		{
			lt8618sx_write_reg(dev, 0x4d, 0x09);
			lt8618sx_write_reg(dev, 0x27, 0x66); //0x06
			lt8618sx_write_reg(dev, 0x28, 0x88); // 0x88
		}
		else
		{
			lt8618sx_write_reg(dev, 0x4d, 0x00);
			lt8618sx_write_reg(dev, 0x27, 0x60); //0x06
			lt8618sx_write_reg(dev, 0x28, 0x00); // 0x88
		}
		debug("\r\n PLL SDR");
	}

	lt8618sx_write_reg(dev, 0xff, 0x81);

	read_val = lt8618sx_read_reg(dev, 0x2b);
	lt8618sx_write_reg(dev, 0x2b, read_val & 0xfd); // sw_en_txpll_cal_en

	read_val = lt8618sx_read_reg(dev, 0x2e);
	lt8618sx_write_reg(dev, 0x2e, read_val & 0xfe); //sw_en_txpll_iband_set

	lt8618sx_write_reg(dev, 0xff, 0x82);
	lt8618sx_write_reg(dev, 0xde, 0x00);
	lt8618sx_write_reg(dev, 0xde, 0xc0);

	lt8618sx_write_reg(dev, 0xff, 0x80);
	lt8618sx_write_reg(dev, 0x16, 0xf1);
	lt8618sx_write_reg(dev, 0x18, 0xdc); //txpll _sw_rst_n
	lt8618sx_write_reg(dev, 0x18, 0xfc);
	lt8618sx_write_reg(dev, 0x16, 0xf3);

	lt8618sx_write_reg(dev, 0xff, 0x81);

	if (priv->chip_ver == CHIP_VER_U3)
	{
		if (priv->use_ddrclk)
		{
			lt8618sx_write_reg(dev, 0x2a, 0x10);
			lt8618sx_write_reg(dev, 0x2a, 0x30);
		}
		else
		{
			lt8618sx_write_reg(dev, 0x2a, 0x00);
			lt8618sx_write_reg(dev, 0x2a, 0x20);
		}
	}

	for (j = 0; j < 0x05; j++)
	{
		mdelay(10);
		lt8618sx_write_reg(dev, 0xff, 0x80);
		lt8618sx_write_reg(dev, 0x16, 0xe3); /* pll lock logic reset */
		lt8618sx_write_reg(dev, 0x16, 0xf3);

		lt8618sx_write_reg(dev, 0xff, 0x82);
		lock = 0x80 & lt8618sx_read_reg(dev, 0x15);
		cali_val = lt8618sx_read_reg(dev, 0xea);
		cali_done = 0x80 & lt8618sx_read_reg(dev, 0xeb);

		if (lock && cali_done && (cali_val != 0xff))
		{

			debug("\r\nTXPLL Lock");
			return;
		}
		else
		{
			lt8618sx_write_reg(dev, 0xff, 0x80);
			lt8618sx_write_reg(dev, 0x16, 0xf1);
			lt8618sx_write_reg(dev, 0x18, 0xdc); //txpll _sw_rst_n
			lt8618sx_write_reg(dev, 0x18, 0xfc);
			lt8618sx_write_reg(dev, 0x16, 0xf3);
			debug("\r\nTXPLL Reset");
		}
	}

	debug("\r\nTXPLL Unlock");
}

#ifdef _DVI_
void LT8618SXB_DVI_setting(struct udevice *dev)
{
	lt8618sx_write_reg(dev, 0xff, 0x82); // register bank
	lt8618sx_write_reg(dev, 0xd6, 0x0e); // bit7 = 0 : DVI output; bit7 = 1: HDMI output
}

#else
void LT8618SXB_Audio_setting(struct udevice *dev)
{
	//----------------IIS-----------------------
	// IIS Input
	lt8618sx_write_reg(dev, 0xff, 0x82); // register bank
	lt8618sx_write_reg(dev, 0xd6, 0x8e); // bit7 = 0 : DVI output; bit7 = 1: HDMI output
	lt8618sx_write_reg(dev, 0xd7, 0x04); // sync polarity

	lt8618sx_write_reg(dev, 0xff, 0x84); // register bank
	lt8618sx_write_reg(dev, 0x06, 0x08);
	lt8618sx_write_reg(dev, 0x07, 0x10); // SD0 channel selected

	lt8618sx_write_reg(dev, 0x09, 0x00); // 0x00 :Left justified; default
	// 0x02 :Right justified;

	//-----------------SPDIF---------------------

	/*	SPDIF Input
	   lt8618sx_write_reg(dev, 0xff, 0x82 );// register bank
	   lt8618sx_write_reg(dev, 0xd6, 0x8e );
	   lt8618sx_write_reg(dev, 0xd7, 0x00 );	//sync polarity

	   lt8618sx_write_reg(dev, 0xff, 0x84 );// register bank
	   lt8618sx_write_reg(dev, 0x06, 0x0c );
	   lt8618sx_write_reg(dev, 0x07, 0x10 );
	*/

	//-------------------------------------------

	lt8618sx_write_reg(dev, 0x0f, 0x0b + Sample_Freq[_48KHz]);

	// 根据音频数据长度的不同，设置不同的寄存器值。
	lt8618sx_write_reg(dev, 0x34, 0xd4); //CTS_N / 2; 32bit
										 //	lt8618sx_write_reg(dev, 0x34, 0xd5 );	//CTS_N / 4; 16bit

	lt8618sx_write_reg(dev, 0x35, (u8)(IIS_N[_48KHz] / 0x10000));
	lt8618sx_write_reg(dev, 0x36, (u8)((IIS_N[_48KHz] & 0x00FFFF) / 0x100));
	lt8618sx_write_reg(dev, 0x37, (u8)(IIS_N[_48KHz] & 0x0000FF));

	lt8618sx_write_reg(dev, 0x3c, 0x21); // Null packet enable
}
#endif

// LT8618SXB only supports three color space convert: YUV422, yuv444 and rgb888.
// Color space convert of YUV420 is not supported.
void LT8618SXB_CSC_setting(struct udevice *dev)
{
	// color space config
	lt8618sx_write_reg(dev, 0xff, 0x82); // register bank
										 //	lt8618sx_write_reg(dev, 0xb9, 0x08 );// YCbCr444 to RGB
										 //	lt8618sx_write_reg(dev, 0xb9, 0x18 );// YCbCr422 to RGB

	//	lt8618sx_write_reg(dev, 0xb9, 0x80 );// RGB to YCbCr444
	//	lt8618sx_write_reg(dev, 0xb9, 0xa0 );// RGB to YCbCr422

	//	lt8618sx_write_reg(dev, 0xb9, 0x10 );// YCbCr422 to YCbCr444
	//	lt8618sx_write_reg(dev, 0xb9, 0x20 );// YCbCr444 to YCbCr422

	lt8618sx_write_reg(dev, 0xb9, 0x00); // No csc
}

#ifdef _HDMI_
void LT8618SXB_AVI_setting(struct udevice *dev)
{
	struct lt8618sx_priv *priv = dev_get_priv(dev);
	u8 vic_num = lt8618sx_format_timing[priv->resolution][Vic];

	//AVI
	u8 AVI_PB0 = 0x00;
	u8 AVI_PB1 = 0x00;
	u8 AVI_PB2 = 0x00;

	/********************************************************************************
	   The 0x43 register is checksums,
	   changing the value of the 0x45 or 0x47 register,
	   and the value of the 0x43 register is also changed.
	   0x43, 0x44, 0x45, and 0x47 are the sum of the four register values is 0x6F.
	 *********************************************************************************/

	// Please refer to function: void LT8618SXB_CSC_setting(struct udevice *dev)

	/****************************************************
	   Because the color space of RGB888 signal is RGB,
	   if lt8618sxb does not do color space convert (no CSC),
	   the color space of output HDMI is RGB.
	 *****************************************************/

	AVI_PB1 = 0x10;												   // PB1,color space: YUV444 0x50;YUV422 0x30; RGB 0x10
	AVI_PB2 = lt8618sx_format_timing[priv->resolution][Pic_Ratio]; // PB2; picture aspect rate
																   //	AVI_PB2 = 0x2A; // PB2; picture aspect rate: 0x2A : 16:9 // Such as : 720P / 1080P / 40K30 / 1600x900
																   //	AVI_PB2 = 0x19; // PB2; picture aspect rate: 0x19 : 4:3 // Such as : 640x480 / 1024x768 / 1280x1024

	AVI_PB0 = ((AVI_PB1 + AVI_PB2 + vic_num) <= 0x6f) ? (0x6f - AVI_PB1 - AVI_PB2 - vic_num) : (0x16f - AVI_PB1 - AVI_PB2 - vic_num);

	lt8618sx_write_reg(dev, 0xff, 0x84);	// register bank
	lt8618sx_write_reg(dev, 0x43, AVI_PB0); // PB0,avi packet checksum
	lt8618sx_write_reg(dev, 0x44, AVI_PB1); // PB1,color space: YUV444 0x50;YUV422 0x30; RGB 0x10
	lt8618sx_write_reg(dev, 0x45, AVI_PB2); // PB2;picture aspect rate: 0x19:4:3 ; 0x2A : 16:9
	lt8618sx_write_reg(dev, 0x47, vic_num); // PB4;vic ,0x10: 1080P ;  0x04 : 720P

	//	lt8618sx_write_reg(dev,0xff,0x84);
	//	8618SXB hdcp1.4 加密的话，要保证hfp + 8410[5:0](rg_island_tr_res) 的个数（video de的下降沿到 最近的一个aude 的间隔），大于58；hdcp1.4 spec中有要求
	lt8618sx_write_reg(dev, 0x10, 0x2c); //data iland
	lt8618sx_write_reg(dev, 0x12, 0x64); //act_h_blank

	//VS_IF, 4k 30hz need send VS_IF packet. Please refer to hdmi1.4 spec 8.2.3
	if (vic_num == 95)
	{
		//	   lt8618sx_write_reg(dev,0xff,0x84);
		lt8618sx_write_reg(dev, 0x3d, 0x2a); //UD1 infoframe enable

		lt8618sx_write_reg(dev, 0x74, 0x81);
		lt8618sx_write_reg(dev, 0x75, 0x01);
		lt8618sx_write_reg(dev, 0x76, 0x05);
		lt8618sx_write_reg(dev, 0x77, 0x49);
		lt8618sx_write_reg(dev, 0x78, 0x03);
		lt8618sx_write_reg(dev, 0x79, 0x0c);
		lt8618sx_write_reg(dev, 0x7a, 0x00);
		lt8618sx_write_reg(dev, 0x7b, 0x20);
		lt8618sx_write_reg(dev, 0x7c, 0x01);
	}
	else
	{
		// lt8618sx_write_reg(dev,0xff,0x84);
		lt8618sx_write_reg(dev, 0x3d, 0x0a); //UD1 infoframe disable
	}
}

#endif

void LT8618SXB_TX_Phy(struct udevice *dev)
{
	// HDMI_TX_Phy
	lt8618sx_write_reg(dev, 0xff, 0x81); // register bank
	lt8618sx_write_reg(dev, 0x30, 0xea);

#if 1 // DC mode
	lt8618sx_write_reg(dev, 0x31, 0x44);
	lt8618sx_write_reg(dev, 0x32, 0x4a);
	lt8618sx_write_reg(dev, 0x33, 0x0b);
#else // AC mode
	lt8618sx_write_reg(dev, 0x31, 0x73);
	lt8618sx_write_reg(dev, 0x32, 0xea);
	lt8618sx_write_reg(dev, 0x33, 0x4a);
#endif

	lt8618sx_write_reg(dev, 0x34, 0x00);
	lt8618sx_write_reg(dev, 0x35, 0x00);
	lt8618sx_write_reg(dev, 0x36, 0x00);
	lt8618sx_write_reg(dev, 0x37, 0x44);
	lt8618sx_write_reg(dev, 0x3f, 0x0f);

	lt8618sx_write_reg(dev, 0x40, 0xa0); //0xa0 -- CLK tap0 swing
	lt8618sx_write_reg(dev, 0x41, 0xa0); //0xa0 -- D0 tap0 swing
	lt8618sx_write_reg(dev, 0x42, 0xa0); //0xa0 -- D1 tap0 swing
	lt8618sx_write_reg(dev, 0x43, 0xa0); //0xa0 -- D2 tap0 swing

	lt8618sx_write_reg(dev, 0x44, 0x0a);
}

// Step:
// 1、Reset LT8618SX
// 2、LT8618SX Initial setting:
void lt8618sx_init(struct udevice *dev)
{
	struct lt8618sx_priv *priv = dev_get_priv(dev);

	priv->use_ddrclk = 0;

	// According to the input different resolution, set the following parameters(Resolution_Num).
	// If there is no corresponding resolution in the array, you need to add it manually.
	priv->resolution = RESOLUTION_1080P60;

	// Before initializing lt8168sxb, you need to enable IIC of lt8618sxb
	lt8618sx_write_reg(dev, 0xff, 0x80); // register bank
	lt8618sx_write_reg(dev, 0xee, 0x01); // enable IIC

	lt8618sx_chip_id(dev); // for debug

	// RST_PD_Init
	LT8618SXB_RST_PD_Init(dev);
	LT8618SXB_TTL_Input_Analog(dev);
	LT8618SXB_TTL_Input_Digtal(dev);

	//Wait for the signal to be stable and decide whether the delay is necessary according to the actual situation
	mdelay(1000); // 等待信号稳定,根据实际情况决定是否需要延时

	LT8618SXB_Video_Check(dev); // For debug
	// PLL setting
	LT8618SXB_PLL_config(dev);

#ifdef _HDMI_
	LT8618SXB_Audio_setting(dev);
#endif

#ifdef _DVI_
	LT8618SXB_DVI_setting(dev);
#endif

	// color space config
	LT8618SXB_CSC_setting(dev);

#ifdef _LT8618_HDCP_
	LT8618SXB_HDCP_Init(dev);
#endif

#ifdef _HDMI_
	//AVI
	LT8618SXB_AVI_setting(dev);
#endif

#ifdef _LT8618_HDCP_
	LT8618SXB_HDCP_Enable(dev);
#endif

#ifdef CONFIG_I2C_EDID
	// This operation is not necessary. Read TV EDID if necessary.
	lt8618sx_get_edid(dev); // Read TV EDID
#endif

#ifndef _DE_SYNC_MODE_
	LT8618SX_Timing_Set(dev);
#endif

	LT8618SXB_RST_PD_Init(dev);
	// HDMI_TX_Phy
	LT8618SXB_TX_Phy(dev);
}

// When the lt8618sxb works, the resolution of the TTL signal changes.
// The following settings need to be configured.
void lt8618sx_resolution_change(struct udevice *dev, u8 Resolution)
{
	struct lt8618sx_priv *priv = dev_get_priv(dev);

	priv->resolution = Resolution;
	LT8618SXB_PLL_config(dev);

#ifndef _DE_SYNC_MODE_
	LT8618SX_Timing_Set(dev);
#endif

#ifdef _HDMI_
	LT8618SXB_AVI_setting(dev);
#endif
}

static int lt8618sx_attach(struct udevice *dev)
{
	int ret;
	struct lt8618sx_priv __maybe_unused *priv = dev_get_priv(dev);

#if CONFIG_IS_ENABLED(DM_GPIO)
	struct gpio_desc reset_gpios;
	ret = gpio_request_by_name(dev, "reset_gpios", 0, &reset_gpios, GPIOD_IS_OUT);
	if (!ret) {
		dm_gpio_set_value(&reset_gpios, 0);
		mdelay(100);
		dm_gpio_set_value(&reset_gpios, 1);
		mdelay(100);
	}
#else
	int reset_gpios = LT8618SX_RESET_GPIO;
	ret = gpio_request(reset_gpios, "lt8618sx_reset");
	if (!ret)
	{
		gpio_direction_output(reset_gpios, 0);
		mdelay(100);
		gpio_direction_output(reset_gpios, 1);
		mdelay(100);
	}
#endif

	ret = video_bridge_set_active(dev, true);
	if (ret)
		return ret;

	lt8618sx_init(dev);

#ifdef CONFIG_I2C_EDID
	edid_print_info((struct edid1_info *)priv->sink_edid);
#endif

	return 0;
}

#ifdef CONFIG_I2C_EDID
static int lt8618sx_read_edid(struct udevice *dev, u8 *buf, int size)
{
	struct lt8618sx_priv *priv = dev_get_priv(dev);
	if (size > EDID_SIZE)
		size = EDID_SIZE;
	memcpy(buf, priv->edid_buf, size);

	return size;
}
#endif

static int lt8618sx_probe(struct udevice *dev)
{
	debug("%s\n", __func__);
	if (device_get_uclass_id(dev->parent) != UCLASS_I2C)
		return -EPROTONOSUPPORT;

	return 0;
}

struct video_bridge_ops lt8618sx_ops = {
	.attach = lt8618sx_attach,
#ifdef CONFIG_I2C_EDID
	.read_edid = lt8618sx_read_edid,
#endif
};

static const struct udevice_id lt8618sx_ids[] = {
	{
		.compatible = "lontiumsemi,lt8618sx",
	},
	{}};

U_BOOT_DRIVER(parade_lt8618sx) = {
	.name = "lontiumsemi_lt8618sx",
	.id = UCLASS_VIDEO_BRIDGE,
	.of_match = lt8618sx_ids,
	.probe = lt8618sx_probe,
	.ops = &lt8618sx_ops,
	.priv_auto_alloc_size = sizeof(struct lt8618sx_priv),
};
